/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package czStvDpds.graphics3D.geometry;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;

/**
 * Ukládá informace o geometrii objektů.
 *
 * @author divad
 */
public class Mesh {

    private FloatBuffer vertices, normals, textureCoords, vboDataSet;
    private IntBuffer VBOIndices;
    private int textureCoordsNumber;
    public static final int NORMAL_MESH = 0, VBO_MESH = 1;
    private int type;

    /**
     * Model určený pro zobrazování pomocí Vertex3f, Normal3f a
     * MultiTexCoordf3f.
     *
     * @param vertices - souřadnice bodů (x,y,z)
     * @param normals - normály v bodech (x,y,z)
     * @param textureCoords - texturové souřadnice v bodech (r,s,t)
     * @param indices - pole s indexi jednotlivých bodů v tom pořadí, jak se
     * budou vykreslovat.
     */
    public Mesh(FloatBuffer vertices, FloatBuffer normals, FloatBuffer textureCoords, IntBuffer indices) {
        this.vertices = vertices;
        this.vertices.rewind();
        this.normals = normals;
        this.normals.rewind();
        this.textureCoords = textureCoords;
        this.textureCoords.rewind();
        this.VBOIndices = indices;
        this.VBOIndices.rewind();
        this.textureCoordsNumber = textureCoords.capacity() / vertices.capacity();
        this.type = Mesh.NORMAL_MESH;
    }

    public Mesh(Mesh mesh) {
        if (mesh != null) {
            if (mesh.type == Mesh.NORMAL_MESH) {
                FloatBuffer vertices = BufferUtils.createFloatBuffer(mesh.getVertices().capacity());
                vertices.put(mesh.getVertices());
                vertices.rewind();
                this.vertices = vertices;
                FloatBuffer normals = BufferUtils.createFloatBuffer(mesh.getNormals().capacity());
                normals.put(mesh.getVertices());
                normals.rewind();
                this.normals = normals;
                FloatBuffer textureCoords = BufferUtils.createFloatBuffer(mesh.getTextureCoords().capacity());
                textureCoords.put(mesh.getTextureCoords());
                textureCoords.rewind();
                this.textureCoords = textureCoords;
                IntBuffer indices = BufferUtils.createIntBuffer(mesh.getVBOIndices().capacity());
                indices.put(mesh.getVBOIndices());
                indices.rewind();
                this.VBOIndices = indices;
                this.textureCoordsNumber = mesh.textureCoordsNumber;
                this.type = Mesh.NORMAL_MESH;
            } else if (mesh.type == Mesh.VBO_MESH) {
                FloatBuffer data = BufferUtils.createFloatBuffer(mesh.getVboDataSet().capacity());
                data.put(mesh.getVboDataSet());
                data.rewind();
                this.vboDataSet = data;
                this.textureCoordsNumber = mesh.textureCoordsNumber;
            }
        }
    }

    /**
     * Model určený pro zobrazení pomocí VBO.
     *
     * @param vboDataSet Pole informací o vertexech uložených ve formátu
     * 3P3N(X*3)T 3P...3 floaty pozice; 3N...3 floaty normála; 3T...3 texturov0
     * koordináty; X..počet texturových koordinátů na bod
     * @param indeces Odkazy na jednotlivé
     * @param textureCoordsNumber
     */
    public Mesh(FloatBuffer vboDataSet, IntBuffer indices, int textureCoordsNumber) {
        this.vboDataSet = vboDataSet;
        this.VBOIndices = indices;
        this.textureCoordsNumber = textureCoordsNumber;
        this.type = Mesh.VBO_MESH;
    }

    /**
     * Vrátí Buffer s normálami
     *
     * @return
     */
    public FloatBuffer getNormals() {
        return normals;
    }

    /**
     * Vrátí Buffer s teturovými koordináty
     *
     * @return
     */
    public FloatBuffer getTextureCoords() {
        return textureCoords;
    }

    /**
     * Vrátí Buffer s body
     *
     * @return
     */
    public FloatBuffer getVertices() {
        return vertices;
    }

    /**
     * Vrátí Buffer s indexi
     *
     * @return
     */
    public IntBuffer getVBOIndices() {
        return VBOIndices;
    }

    /**
     * Vrátí počet texturových koordinátů.
     *
     * @return
     */
    public int getTextureCoordsNumber() {
        return textureCoordsNumber;
    }

    /**
     * Vrátí pole informací o vertexech.
     *
     * @return
     */
    public FloatBuffer getVboDataSet() {
        return vboDataSet;
    }

    /**
     * Vrátí typ modelu.
     *
     * @return
     */
    public int getType() {
        return type;
    }

    /**
     * Nastaví Buffer s normálami
     *
     * @return
     */
    public void setNormals(FloatBuffer normals) {
        this.normals = normals;
        this.normals.rewind();
    }

    /**
     * Nastaví Buffer s texturovými koordináty
     *
     * @return
     */
    public void setTextureCoords(FloatBuffer textureCoords) {
        this.textureCoords = textureCoords;
        this.textureCoords.rewind();
    }

    /**
     * Nastaví Buffer s body
     *
     * @return
     */
    public void setVertices(FloatBuffer vertices) {
        this.vertices = vertices;
        this.vertices.rewind();
    }

    /**
     * Nastaví Buffer s indexi
     *
     * @return
     */
    public void setVBOIndices(IntBuffer indices) {
        this.VBOIndices = indices;
        this.VBOIndices.rewind();
    }

    /**
     * Nastaví pole informací o vertexech a počet texturových koordinátů.
     *
     * @param vboDataSet
     */
    public void setVboDataSet(FloatBuffer vboDataSet, int textureCoordsNumber) {
        this.vboDataSet = vboDataSet;
        this.textureCoordsNumber = textureCoordsNumber;
    }

    /**
     * Převede model na VBO (zobrazovaný pomocí VBO) a "zapomene" jednotlivé
     * buffery.
     *
     * @return vrací true pokud bylo vytvoření VBO úspěšné
     */
    public void toVBO() {
        int VALUES_PER_VERTEX = 3;
        int VALUES_PER_NORMAL = 3;
        int VALUES_PER_TEXCOORD = 3;

        this.vboDataSet = BufferUtils.createFloatBuffer(this.vertices.capacity() + this.normals.capacity() + this.textureCoords.capacity());

        for (int index = 0; index < this.vertices.capacity() / VALUES_PER_VERTEX; index++) {

            //první­ 3 jsou pozice vertexu
            this.vboDataSet.put(this.vertices.get(index * VALUES_PER_VERTEX));
            this.vboDataSet.put(this.vertices.get(index * VALUES_PER_VERTEX + 1));
            this.vboDataSet.put(this.vertices.get(index * VALUES_PER_VERTEX + 2));

            //druhé 3 jsou normála vertexu
            this.vboDataSet.put(this.normals.get(index * VALUES_PER_NORMAL));
            this.vboDataSet.put(this.normals.get(index * VALUES_PER_NORMAL + 1));
            this.vboDataSet.put(this.normals.get(index * VALUES_PER_NORMAL + 2));

            //3 texturové koordináty na vertex
            for (int i = 0; i < textureCoordsNumber; i++) {
                this.vboDataSet.put(this.textureCoords.get((index * VALUES_PER_TEXCOORD * textureCoordsNumber) + (i * VALUES_PER_TEXCOORD)));
                this.vboDataSet.put(this.textureCoords.get((index * VALUES_PER_TEXCOORD * textureCoordsNumber) + (i * VALUES_PER_TEXCOORD) + 1));
                this.vboDataSet.put(this.textureCoords.get((index * VALUES_PER_TEXCOORD * textureCoordsNumber) + (i * VALUES_PER_TEXCOORD) + 2));
            }
        }
        this.vboDataSet.rewind();
        this.type = Mesh.VBO_MESH;
        this.normals = null;
        this.vertices = null;
        this.textureCoords = null;
    }

    /**
     * Převede model na více polí s jednotlivými údaji a "zapomene" pole se
     * všemi údaji najednou.
     */
    public void toNormal() {
        int range = 3 + 3 + (this.textureCoordsNumber * 3);
        this.vertices = BufferUtils.createFloatBuffer(this.vboDataSet.capacity() / range);
        this.normals = BufferUtils.createFloatBuffer(this.vboDataSet.capacity() / range);
        this.textureCoords = BufferUtils.createFloatBuffer((this.vboDataSet.capacity() / range) * this.textureCoordsNumber);
        this.vboDataSet.rewind();
        for (int index = 0; index < this.vboDataSet.capacity(); index += range) {
            for (int i = 0; i < 3; i++) {
                this.vertices.put(this.vboDataSet.get());
            }

            for (int i = 0; i < 3; i++) {
                this.normals.put(this.vboDataSet.get());
            }
            for (int t = 0; t < this.textureCoordsNumber; t++) {
                for (int i = 0; i < 3; i++) {
                    this.textureCoords.put(this.vboDataSet.get());
                }
            }
        }
        this.vertices.rewind();
        this.normals.rewind();
        this.textureCoords.rewind();

        this.vboDataSet = null;

        this.type = Mesh.NORMAL_MESH;
    }

    public void changeVBOIndices(int offset, IntBuffer indices) {
        if (indices != null && offset >= 0 && offset + indices.capacity() <= this.VBOIndices.capacity()) {
            int position = indices.position();
            this.VBOIndices.position(offset);
            for (int i = 0; i < indices.remaining(); i++) {
                this.VBOIndices.put(indices.get());
            }
            indices.position(position);
            this.VBOIndices.rewind();
        }
    }

    public void changeVBODataSet(int offset, FloatBuffer data) {
        if (data != null & offset >= 0 && offset + data.capacity() <= this.vboDataSet.capacity() && data.capacity() % ((6 + (this.textureCoordsNumber * 3)) * 4) == 0) {
            int position = data.position();
            this.vboDataSet.position(offset);
            for (int i = 0; i < data.remaining(); i++) {
                this.vboDataSet.put(data.get());
            }
            data.position(position);
            this.vboDataSet.rewind();
        }
    }
}
